package org.mparts.server.utils;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;

//import org.mparts.server.EMF;
import org.mparts.server.domain.EntityBase;

import com.sencha.gxt.data.shared.SortDir;
import com.sencha.gxt.data.shared.SortInfoBean;
import com.sencha.gxt.data.shared.loader.FilterConfigBean;

public class Paginate<T extends EntityBase> {

	private List<FilterConfigBean> filterConfig;

	// private EntityManager em;

	public Paginate() {
		// em = EMF.get().createEntityManager();
	}

	public List<T> paginate(Class<T> clazz, int offset, int limit,
			List<SortInfoBean> sortInfo, List<FilterConfigBean> filterConfig) {
		this.filterConfig = filterConfig;
		EntityManager em = PersistUtils.getEntityManager();
		List<T> result = null;
		// try {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<T> c = cb.createQuery(clazz);
		Root<T> r = c.from(clazz);

		c.where(condition(cb, r).toArray(new Predicate[] {}));

		if (sortInfo.size() == 0)
			c.orderBy(cb.desc(r.get("id")));
		else {
			if (sortInfo.get(0).getSortDir() == SortDir.ASC)
				c.orderBy(cb.asc(r.get(sortInfo.get(0).getSortField())));
			if (sortInfo.get(0).getSortDir() == SortDir.DESC)
				c.orderBy(cb.desc(r.get(sortInfo.get(0).getSortField())));
		}

		TypedQuery<T> q = em.createQuery(c);
		if (offset >= 0) {
			q.setFirstResult(offset);
		}
		if (limit >= 0) {
			q.setMaxResults(limit);
		}
		result = q.getResultList();
		// } finally {
		// em.close();
		// }
		return result;
	}

	public Long count(Class<T> clazz) {
		EntityManager em = PersistUtils.getEntityManager();
		Long result = 0L;
		// try {
		CriteriaBuilder cb = em.getCriteriaBuilder();
		CriteriaQuery<Long> c = cb.createQuery(Long.class);
		Root<T> r = c.from(clazz);
		c.where(condition(cb, r).toArray(new Predicate[] {}));
		c.select(cb.count(r));
		result = em.createQuery(c).getSingleResult();
		// } finally {
		// em.close();
		// }
		return result;
	}

	private List<Predicate> condition(CriteriaBuilder cb, Root<T> r) {
		List<Predicate> predicates = new ArrayList<Predicate>();
		for (FilterConfigBean s : filterConfig) {

			if ("contains".equals(s.getComparison())) {
				predicates.add(cb.like(cb.lower(r.<String> get(s.getField())),
						"%" + s.getValue().toLowerCase() + "%"));
			} else if ("gt".equals(s.getComparison())) {
				predicates.add(cb.gt(r.<Double> get(s.getField()),
						Double.valueOf(s.getValue())));
			} else if ("lt".equals(s.getComparison())) {
				predicates.add(cb.lt(r.<Double> get(s.getField()),
						Double.valueOf(s.getValue())));
			} else if ("eq".equals(s.getComparison())) {
				predicates.add(cb.equal(r.<Double> get(s.getField()),
						Double.valueOf(s.getValue())));
			} else if ("on".equals(s.getComparison())) {
			} else if ("after".equals(s.getComparison())) {
			} else if ("before".equals(s.getComparison())) {
			} else if ("wide".equals(s.getComparison()) && s.getField().length() > 0) {
				String[] fields = s.getField().split(":");
				List<Predicate> wList = new ArrayList<Predicate>();
				for( String field : fields ) {
					wList.add(cb.or(cb.like(
							cb.lower(r.<String> get(field)), "%"
									+ s.getValue().toLowerCase() + "%")));
				}
				predicates.add(cb.or(wList.toArray(new Predicate[] {})));
			}

		}
		return predicates;
	}
}
